# encoding=utf-8
import cv2
import numpy as np
from seg_system.application_config import ApplicationConfig
from seg_cuda.CudaMatrixTools.Builder.BuilderCommon import BuilderCommon


class MatBuilder:
    """MatBuilder:
        - 从tset_seg_cuda/test_cuda_mat_read可以看到，cuda对大矩阵的处理时间需求变化不大
        - 同时在c++段控制numpy的数据流进行拼接过于麻烦，因此在python端进行拼接

        - 本方案针对 *** opencv的mat ***数据结构而实现
        - 希望在后续加入cuda stream以大幅度减少传输过程带来的性能损失
    """

    @staticmethod
    def build(mat_list: list, stride: int = 0,
              to_cuda: bool = ApplicationConfig.SystemConfig.USER_CUDA_CV, **kwargs):
        """
            :param mat_list: 图像列表
            :param stride: 图像矩阵间隔，根据算法而定，防止运算的时候，部分算法跨图片像素操作
                           此间隔将会在split中被删除
            :param to_cuda: 是否转换到cuda中
        """
        BuilderCommon.ShapeTest(mat_list)
        big_mat_shape = BuilderCommon.MinRectangle(len(mat_list))
        t_s1, t_s2 = mat_list[0].shape, big_mat_shape.shape
        build_big_matrix = BuilderCommon.BuildBigMatrix(t_s1, t_s2[0], t_s2[1], stride)

        for i in range(len(big_mat_shape)):
            for j in range(len(big_mat_shape[i])):
                d = big_mat_shape[i, j]
                if d == 0:
                    continue

                h_start = t_s1[0] * i + i * stride
                h_end = h_start + t_s1[0]
                w_start = t_s1[1] * j + j * stride
                w_end = w_start + t_s1[1]

                build_big_matrix[h_start: h_end, w_start: w_end] = mat_list[i * len(big_mat_shape[0]) + j]

        if to_cuda:
            # 暂时没有实现直接传递GpuMat，所以需要调用时候填写False
            build_big_matrix = BuilderCommon.UploadMat(build_big_matrix)

        return build_big_matrix, big_mat_shape

    @staticmethod
    def split(mat, mat_shape: np.ndarray, each_mat_shape: tuple, stride: int = 0, **kwargs):
        """
            :param mat: 大图像矩阵，如果是GPU类型(opencv)将会下载
            :param mat_shape: 每个区域块是否存在有效像素
            :param each_mat_shape: 每个区域块的图像尺寸
            :param stride: 每个小图像间的间隔

            notice:
                - 如果是GpuMat类型，将会下载到本地并返回
        """
        array_mat = BuilderCommon.DownloadMat(mat)
        matrix_list = []
        for i in range(len(mat_shape)):
            for j in range(len(mat_shape[i])):
                d = mat_shape[i, j]
                if d == 0:
                    return matrix_list

                h_start = each_mat_shape[0] * i + i * stride
                h_end = h_start + each_mat_shape[0]
                w_start = each_mat_shape[1] * j + j * stride
                w_end = w_start + each_mat_shape[1]

                matrix_list.append(array_mat[h_start: h_end, w_start: w_end])

        return matrix_list

